package bg.btanev.eightyfour.repository.impl;

import java.util.List;

import org.apache.commons.lang.Validate;
import org.hibernate.Criteria;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import bg.btanev.eightyfour.dao.GenericDAO;
import bg.btanev.eightyfour.dao.PagingConfig;
import bg.btanev.eightyfour.dao.PagingConfig.OrderConfig;
import bg.btanev.eightyfour.dao.exception.AttachEntityException;
import bg.btanev.eightyfour.domain.Target;
import bg.btanev.eightyfour.domain.person.Person;
import bg.btanev.eightyfour.repository.TargetRepository;
import bg.btanev.eightyfour.shared.state.TargetState;

/**
 *
 * @author B.Tanev
 *
 */
public class TargetRepositoryImpl implements TargetRepository {
	/**
	 * Logger for this class
	 */
	private static final Logger logger = LoggerFactory.getLogger(TargetRepositoryImpl.class);

	private GenericDAO<Long, Target> targetDAO;

	public TargetRepositoryImpl() {
	}

	public void setTargetDAO(GenericDAO<Long, Target> targetDAO) {
		this.targetDAO = targetDAO;
	}

	@Override
	public Target createAndStoreTarget(Person person) {
		logger.debug("createAndStoreTarget(Person) - start"); //$NON-NLS-1$

		Target target = new Target(person);
		storeTarget(target);

		logger.debug("createAndStoreTarget(Person) - end"); //$NON-NLS-1$
		return target;
	}

	@Override
	public List<Target> findAllTargets() {
		logger.debug("findAllTargets() - start"); //$NON-NLS-1$

		List<Target> result = targetDAO.findAll(false);

		logger.debug("findAllTargets() - end"); //$NON-NLS-1$
		return result;
	}

	@Override
	public List<Target> findTargets(String email, String fName, String lName) {
		DetachedCriteria detachedCriteria = targetDAO.createDetachedCriteria();

		detachedCriteria.createAlias("person", "p", Criteria.LEFT_JOIN);
		if (fName != null) {
			detachedCriteria.add(Restrictions.ilike("p.personName.firstName", fName, MatchMode.ANYWHERE));
		}

		if (lName != null) {
			detachedCriteria.add(Restrictions.ilike("p.personName.lastName", lName, MatchMode.ANYWHERE));
		}

		if (email != null) {
			detachedCriteria.add(Restrictions.ilike("p.contactInfo.email", email, MatchMode.ANYWHERE));
		}

		detachedCriteria.createAlias("status", "s", Criteria.LEFT_JOIN);
		PagingConfig pagingConfig = new PagingConfig(-1, -1);
		pagingConfig.addOrder(new OrderConfig("s.lastUpdated", true));

		return targetDAO.findByCriteria(detachedCriteria, pagingConfig);
	}

	@Override
	public List<Target> findAllTargetsToUpdate() {
		logger.debug("findAllTargetsToUpdate() - start"); //$NON-NLS-1$

		DetachedCriteria detachedCriteria = targetDAO.createDetachedCriteria();

		detachedCriteria.createAlias("status", "s", Criteria.LEFT_JOIN);
		detachedCriteria.add(Restrictions.eq("s.state", TargetState.ACTIVE));
		detachedCriteria.add(Restrictions.in("s.updateState", new TargetState[] { TargetState.INITIALIZED,
				TargetState.UPDATED }));
		// detachedCriteria.add(Restrictions.le("s.nextUpdate", new Date()));
		detachedCriteria.addOrder(Order.asc("s.nextUpdate"));

		List<Target> returnList = targetDAO.findByCriteria(detachedCriteria);

		logger.debug("findAllTargetsToUpdate() - end"); //$NON-NLS-1$
		return returnList;
	}

	@Override
	public void removeTarget(Target target) {
		logger.debug("removeTarget(Target) - start"); //$NON-NLS-1$

		targetDAO.delete(target);

		logger.debug("removeTarget(Target) - end"); //$NON-NLS-1$
	}

	@Override
	public void storeTarget(Target target) {
		logger.debug("storeTarget(Target) - start"); //$NON-NLS-1$

		Validate.notNull(target);
		if (target.getId() != null) {
			targetDAO.merge(target);
		}

		targetDAO.saveUpdateEntity(target);

		logger.debug("storeTarget(Target) - end"); //$NON-NLS-1$
	}

	@Override
	public Target findByPerson(Person person) {
		logger.debug("findByPerson(Person) - start"); //$NON-NLS-1$

		Target returnTarget = targetDAO.findByUniqueProperty("person", person);

		logger.debug("findByPerson(Person) - end"); //$NON-NLS-1$
		return returnTarget;
	}

	@Override
	public Target findByIdentifier(Long id) {
		return targetDAO.findById(id);
	}

	@Override
	public void clearLocalCache() {
		targetDAO.clearSession();
	}

	@Override
	public void flushLocalCache() {
		targetDAO.flush();
	}

	@Override
	public void removeFromLocalCache(Target target) {
		targetDAO.detach(target);
	}

	@Override
	public Target reassociateTarget(Target target) {
		logger.debug("reassociateTarget(Target) - start"); //$NON-NLS-1$

		Target reassociatedTarget = null;
		try {
			reassociatedTarget = targetDAO.attach(target);
		} catch (AttachEntityException e) {
			logger.error("reassociateTarget(Target)", e); //$NON-NLS-1$
			reassociatedTarget = null;
		}

		logger.debug("reassociateTarget(Target) - end"); //$NON-NLS-1$
		return reassociatedTarget;
	}
}
